package com.hup.utils.swing.customView;

import com.hup.utils.commons.TextUtil;
import com.hup.utils.commons.hardware.SystemUtil;
import com.hup.utils.swing.ComponentUtil;
import com.hup.utils.swing.DimensionUtil;
import com.hup.utils.swing.customView.BaseFileChooser.ChoseListener;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.File;

/**
 * 文件选择panel;
 *
 * @author hugan
 * @date 2019/12/6
 */
@Log4j2
public class FileChooserPanel extends JPanel implements ChoseListener {
    private static final String MOUSE_TIPS = "双击选择文件";

    private JLabel labInfo;
    private FileText txtFile;

    /**
     * txt无选中文件时的默认大小
     */
    private Dimension DEF_TXT_SIZE;

    /**
     * 上次最后打开的路径
     */
    private String recentPath;

    /**
     * 左侧文本标签内容
     */
    @Getter
    private String label = "请选择";
    /**
     * 弹窗选择窗口中,确认按钮的文本,null不设置时,采用{@link #label}的文本
     */
    @Setter
    @Getter
    private String chooserBtnTxt;
    /**
     * 只能选择ext中的文件类型;只有exts为空,这个字段才生效;
     * 格式:小写字母,不带点
     */
    @Setter
    @Getter
    private String ext;

    /**
     * 只能选择exts中的文件类型;
     * 格式:小写字母,不带点
     */
    @Setter
    @Getter
    private String[] exts;
    /**
     * 当前选中的文件路径,nullable
     */
    @Getter
    private String selectedFilePath;
    /**
     * 当前选中的文件的父目录,nullable
     */
    private String selectedFileParentPath;

    /**
     * 只有控件可见时,才会触发回调
     */
    @Setter
    @Getter
    private ChoseListener choseListener;

    @Setter
    @Getter
    private String hint = MOUSE_TIPS;
    /**
     * true-选择框中,仅显示文件名;
     * tooltips显示全路径
     */
    @Setter
    @Getter
    private boolean showFileNameOnly = true;
    @Setter
    @Getter
    private FileSelectionMode fileSelectionMode = FileSelectionMode.FILES_ONLY;


    public FileChooserPanel() {
        //GridBagLayout实现不换行的FlowLayout
        setLayout(new GridBagLayout());
        labInfo = new JLabel(label);
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(labInfo, gbc);

        txtFile = new FileText();
        txtFile.setToolTipText(MOUSE_TIPS);

        DEF_TXT_SIZE = DimensionUtil.getSizeByText(txtFile, MOUSE_TIPS);
        gbc = new GridBagConstraints();
        gbc.gridx = 2;//这是第3列,addMiddle方法中是把组件添加到第2列
        gbc.gridy = 0;
        gbc.weightx = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(txtFile, gbc);

        updateTxtSize(DEF_TXT_SIZE);
    }

    /**
     * swing无奈的坑: 对于JTextField,setSize无任何作用的;
     * setPreferredSize只在父容器足够宽时才使用,setMinimumSize只在父容器不够宽时才使用!!!
     * 两个都是[只在],如果漏了设置一个,则会在另一种情况时 让view的长度是0,
     * 非常无语
     */
    private void updateTxtSize(Dimension dimension) {
        txtFile.setMinimumSize(dimension);
        txtFile.setPreferredSize(dimension);
        //validate();//只需要调用父容器的即可
        Container parent = getParent();
        if (parent != null) {
            parent.validate();
        }
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        txtFile.setEnabled(enabled);
    }

    public void setLabel(String str) {
        label = str;
        labInfo.setText(label);
    }

    /**
     * 由 {@link FileText#getChoseListener()} 建立关联
     * 实际仅由 {@link BaseFileChooser} 调用
     *
     * @param chosePath notNull, 弹窗有选择文件后才会到达此处
     */
    @Override
    public void afterChose(String chosePath) {
        selectedFilePath = chosePath;
        txtFile.setToolTipText(selectedFilePath);
        File file = new File(selectedFilePath);
        selectedFileParentPath = file.isDirectory() ? selectedFilePath : file.getParent();
        if (selectedFileParentPath == null) selectedFileParentPath = selectedFilePath;//选择了[盘符]
        //
        String textToShow = showFileNameOnly ? file.getName() : selectedFilePath;
        if (textToShow.isEmpty()) textToShow = selectedFilePath;//选择了[盘符]
        txtFile.setText(textToShow);
        Dimension dimension = DimensionUtil.getSizeByText(txtFile, textToShow);
        updateTxtSize(dimension);

        if (choseListener != null && isShowing()) {
            choseListener.afterChose(chosePath);
        }
    }

    /**
     * 外部设置/初始化路径时使用;
     * 会触发回调
     */
    public void setSelectedFilePath(String path) {
        if (!TextUtil.isBlank(path)) {
            recentPath = path;//需要处理,否则在initConfig后,打开窗口时没有历史路径
            afterChose(path);
        } else {
            recentPath = null;
            selectedFilePath = null;
            selectedFileParentPath = null;
            txtFile.setText(null);
            txtFile.setToolTipText(MOUSE_TIPS);
            updateTxtSize(DEF_TXT_SIZE);

            if (choseListener != null && isShowing()) {
                choseListener.afterChose(null);
            }
        }
    }

    /**
     * 把c添加到中间位置,目前没有限制调用次数
     */
    public void addMiddle(JComponent c) {
        add(c, 1);
    }

    /**
     * 当前选中的文件名
     */
    public String getSelectedFileShortName() {
        return txtFile.getText();
    }

    /**
     * 实现文件选择和hint效果功能
     */
    private class FileText extends JTextField implements BaseFileChooser, BaseHintText {

        private Color defBackground;

        private FileText() {
            defBackground = getBackground();
            setEditable(false);
            //由于是不能编辑,所以要手工设置背景,enable也是
            setBackground(defBackground);
            initFileChooserListener();

            final String CMD_OPEN_DIR = "打开目录";
            final String CMD_CLEAR_SELECTION = "清除已选";
            JPopupMenu menu = new JPopupMenu();
            menu.add(new JMenuItem(CMD_OPEN_DIR));
            menu.add(new JMenuItem(CMD_CLEAR_SELECTION));
            ComponentUtil.addMenu(this, menu, e -> {
                switch (e.getActionCommand()) {
                    case CMD_OPEN_DIR:
                        if (selectedFileParentPath != null) {
                            SystemUtil.openDir(selectedFileParentPath);
                        }
                        return;
                    case CMD_CLEAR_SELECTION:
                        if (selectedFilePath != null) {
                            setSelectedFilePath(null);
                        }
                        return;
                }
            });
        }

        @Override
        public int getFileSelectionMode() {
            return fileSelectionMode.value;
        }

        @Override
        public void setEnabled(boolean enabled) {
            super.setEnabled(enabled);
            if (enabled) {
                setBackground(defBackground);
            }
        }

        @Override
        public String getHint() {
            return hint;
        }

        @Override
        public void paint(Graphics g) {
            //paint.st -> paintComponent.st,ed -> paint.st
            super.paint(g);
            paintHint(this, g);
        }

        @Override
        public String getChooserBtnTxt() {
            return chooserBtnTxt == null ? label : chooserBtnTxt;
        }

        @Override
        public String getRecentPath() {
            return recentPath;
        }

        @Override
        public void setRecentPath(String recentPath) {
            FileChooserPanel.this.recentPath = recentPath;
        }

        @Override
        public String getExtFilterText() {
            if (exts != null) return TextUtil.getText(exts, ",");
            if (ext != null) return ext;
            return null;
        }

        @Override
        public boolean isFileMatch(File file) {
            String fileName = file.getName().toLowerCase();
            if (exts != null) {
                for (String ext : exts) {
                    if (fileName.endsWith("." + ext)) {
                        return true;
                    }
                }
                return false;
            } else {
                return ext == null || fileName.endsWith("." + ext);
            }
        }

        @Override
        public ChoseListener getChoseListener() {
            return FileChooserPanel.this;
        }

        @Override
        public void setChoseListener(ChoseListener choseListener) {
            throw new IllegalArgumentException("不能调用此方法");
        }
    }

    /**
     * 支持idea.GUI
     */
    public enum FileSelectionMode {
        FILES_ONLY(JFileChooser.FILES_ONLY),
        DIRECTORIES_ONLY(JFileChooser.DIRECTORIES_ONLY),
        FILES_AND_DIRECTORIES(JFileChooser.FILES_AND_DIRECTORIES);
        public final int value;

        FileSelectionMode(int value) {
            this.value = value;
        }
    }

}
